package cz.data.domain.market.integration.service.impl;

import org.apache.commons.lang3.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import cz.data.common.core.DataConstant;
import cz.data.common.exception.DataException;
import cz.data.domain.market.integration.dao.ServiceIntegrationDao;
import cz.data.domain.market.integration.service.ServiceExecuteService;
import cz.data.domain.market.model.dto.ServiceExecuteDto;
import cz.data.domain.market.model.dto.ServiceReqParam;
import cz.data.domain.market.model.entity.ServiceIntegrationEntity;
import cz.data.domain.market.model.enums.ReqMethod;
import cz.data.domain.market.model.enums.ServiceType;
import feign.Client;
import feign.Request;
import feign.Response;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URI;
import java.util.*;

@Service
public class ServiceExecuteServiceImpl implements ServiceExecuteService {

    @Autowired
    private ServiceIntegrationDao serviceIntegrationDao;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private Client client;

    @Override
    public Object execute(ServiceExecuteDto execute, HttpServletRequest request) {
        // 密钥校验
        String serviceKey = request.getHeader("service_key");
        String secretKey = request.getHeader("secret_key");
        if (StringUtils.isBlank(serviceKey) || StringUtils.isBlank(secretKey)) {
            throw new DataException("service_key或secret_key空");
        }
        if (StringUtils.isBlank(execute.getServiceNo())) {
            throw new DataException("服务编号不能为空");
        }
        ServiceIntegrationEntity serviceIntegrationEntity = serviceIntegrationDao.selectOne(Wrappers.<ServiceIntegrationEntity>lambdaQuery()
                .eq(ServiceIntegrationEntity::getServiceNo, execute.getServiceNo()));
        if (serviceIntegrationEntity == null) {
            throw new DataException("找不到服务：" + execute.getServiceNo());
        }
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        HttpHeaders headers = new HttpHeaders();
        ResponseEntity<String> response = null;
        if (ServiceType.HTTP.getKey().equals(serviceIntegrationEntity.getServiceType())) {
            String header = execute.getHeader();
            String param = execute.getParam();
            if (StringUtils.isNotBlank(header)) {
                try {
                    Map<String, Object> headerMap = objectMapper.readValue(header, new TypeReference<HashMap<String, Object>>() {
                    });
                    for (Map.Entry<String, Object> entry : headerMap.entrySet()) {
                        headers.add(entry.getKey(), String.valueOf(entry.getValue()));
                    }
                } catch (JsonProcessingException e) {
                    throw new DataException("请求头格式有问题");
                }
            }
            if (StringUtils.isNotBlank(param)) {
                try {
                    Map<String, Object> paramMap = objectMapper.readValue(param, new TypeReference<HashMap<String, Object>>() {
                    });
                    for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
                        params.add(entry.getKey(), entry.getValue());
                    }
                } catch (JsonProcessingException e) {
                    throw new DataException("请求参数格式有问题");
                }
            }
            // http服务
            HttpMethod httpMethod = HttpMethod.GET;
            if (ReqMethod.POST.getDesc().equals(serviceIntegrationEntity.getHttpService().getHttpMethod())) {
                httpMethod = HttpMethod.POST;
            }
            HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(params, headers);
            try {
                response = restTemplate.exchange(serviceIntegrationEntity.getHttpService().getUrl(), httpMethod, httpEntity, String.class);
            } catch (Exception e) {
                throw new DataException(e.getMessage());
            }
        } else if (ServiceType.WEBSERVICE.getKey().equals(serviceIntegrationEntity.getServiceType())) {
            // webservice服务
            headers.add("SOAPAction", serviceIntegrationEntity.getWebService().getTargetNamespace() + serviceIntegrationEntity.getWebService().getMethod());
            headers.add("Content-Type", "text/xml;charset=UTF-8");
            HttpEntity<String> entity = new HttpEntity<>(execute.getSoap(), headers);
            try {
                response = restTemplate.exchange(serviceIntegrationEntity.getWebService().getWsdl(), HttpMethod.POST, entity, String.class);
            } catch (Exception e) {
                throw new DataException(e.getMessage());
            }
        }
        return Optional.ofNullable(response).map(ResponseEntity::getBody).orElse(null);
    }

    @Override
    public Object execute(String serviceNo, Map<String, Object> requestParams) {
        ServiceIntegrationEntity service = serviceIntegrationDao.selectOne(Wrappers.<ServiceIntegrationEntity>lambdaQuery()
                .eq(ServiceIntegrationEntity::getServiceNo, serviceNo));
        Objects.requireNonNull(service, "找不到服务：" + serviceNo);
        if (ServiceType.HTTP.getKey().equals(service.getServiceType())) {
            Map<String, Object> pathMap = new HashMap<>();
            Map<String, Object> headerMap = new HashMap<>();
            Map<String, Object> bodyMap = new HashMap<>();
            List<ServiceReqParam> reqParams = service.getReqParams();
            for (ServiceReqParam reqParam : reqParams) {
                for (Map.Entry<String, Object> entry : requestParams.entrySet()) {
                    if (StringUtils.equals(reqParam.getParamName(), entry.getKey())) {
                        if (DataConstant.SERVICE_EXT.RequestParamType.PATH.equals(reqParam.getParamType())) {
                            pathMap.put(entry.getKey(), entry.getValue());
                        } else if (DataConstant.SERVICE_EXT.RequestParamType.HEADER.equals(reqParam.getParamType())) {
                            headerMap.put(entry.getKey(), entry.getValue());
                        } else if (DataConstant.SERVICE_EXT.RequestParamType.BODY.equals(reqParam.getParamType())) {
                            bodyMap.put(entry.getKey(), entry.getValue());
                        }
                    }
                }
            }
            return httpService(service.getHttpService().getUrl(),
                    service.getHttpService().getHttpMethod(),
                    pathMap, headerMap, bodyMap);
        }
        return null;
    }

    private String httpServiceV2(String url, String method,
                                 Map<String, Object> requestPathParams,
                                 Map<String, Object> requestHeaders,
                                 Map<String, Object> requestBody) {
        URI uri = restTemplate.getUriTemplateHandler()
                .expand(url, Optional.ofNullable(requestPathParams).orElseGet(HashMap::new));
        Map<String, Collection<String>> headers = new HashMap<>();
        if (MapUtils.isNotEmpty(requestHeaders)) {
            requestHeaders.forEach((key, value) -> headers.put(key, Collections.singletonList(String.valueOf(value))));
        }
        Request.Body body = null;
        if (Objects.nonNull(requestBody)) {
            body = Request.Body.create(JSONObject.toJSONString(requestBody));
        }
        Request request = Request.create(Request.HttpMethod.valueOf(method), uri.toString(), headers, body, null);
        Request.Options options = new Request.Options();
        try (Response response = client.execute(request, options)) {
            try (Response.Body responseBody = response.body()) {
                return objectMapper.readValue(responseBody.asInputStream(), String.class);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private String httpService(String url, String method,
                               Map<String, Object> pathMap,
                               Map<String, Object> requestHeaders,
                               Map<String, Object> requestBody) {
        URI uri = restTemplate.getUriTemplateHandler()
                .expand(url, Optional.ofNullable(pathMap).orElseGet(HashMap::new));
        HttpEntity<Map<String, Object>> httpEntity = new HttpEntity<>(
                requestBody, mapConvertMultiValueMap(requestHeaders)
        );

        ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.valueOf(method), httpEntity, String.class);
        int statusCodeValue = response.getStatusCodeValue();
        return Optional.of(response)
                .map(ResponseEntity::getBody)
                .orElseThrow(() -> new DataException("服务错误,响应状态:" + statusCodeValue));
    }

    private MultiValueMap<String, String> mapConvertMultiValueMap(Map<String, Object> map) {
        if (Objects.isNull(map))
            return null;
        MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<>();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getValue() instanceof Collection) {
                Collection<?> collection = (Collection<?>) entry.getValue();
                for (Object ele : collection) {
                    multiValueMap.add(entry.getKey(), Optional.ofNullable(ele).map(Object::toString).orElse(""));
                }
            } else {
                multiValueMap.set(entry.getKey(), Optional.ofNullable(entry.getValue()).map(Object::toString).orElse(""));
            }
        }
        return multiValueMap;
    }
}
